%%capture
#!pip install bayesian-optimization
%%capture
import math
import seaborn as sns
import pandas as pd
import numpy as np
import random
import altair as alt
%matplotlib inline
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.subplots as sp
import matplotlib.gridspec as gridspec
from plotly.subplots import make_subplots
import plotly.graph_objects as go
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso, LogisticRegression #modelamiento
from sklearn.cluster import KMeans
from sklearn.mixture import GaussianMixture
from sklearn.feature_selection import SelectFromModel
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis
from sklearn.decomposition import PCA
from IPython.display import display
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer #missing value imputation
from sklearn.svm import SVC
# from bayes_opt import BayesianOptimization
from sklearn.metrics import roc_auc_score,roc_curve
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import StratifiedKFold
from sklearn.neural_network import MLPClassifier
# import warnings filter
from warnings import simplefilter
# ignore all future warnings
simplefilter(action='ignore', category=FutureWarning)
simplefilter(action='ignore', category=UserWarning)
pd.set_option('display.float_format', '{:,.2f}'.format)
np.seterr(divide = 'ignore')
# Supress warnings
import warnings
warnings.filterwarnings("ignore")
import matplotlib.ticker as mtick ## función para formatear en visualizaciones
from scipy.stats import skew, kurtosis ## funciones estadísticas
from sklearn.impute import KNNImputer
from sklearn.preprocessing import LabelEncoder
import matplotlib.ticker as mtick ## función para formatear en visualizaciones
from scipy.stats import norm ## distribución normal
from scipy.stats import poisson ## distribución Poisson
from scipy.stats import t ## distribución t
from scipy.stats import f ## distribución F
from scipy.stats import ttest_1samp ## Prueba t una población
from scipy.stats import ttest_ind ## Prueba t comparación medias
from scipy.stats import shapiro ## Prueba normalidad Shapiro-Wilks
from scipy.stats import anderson ## Prueba normalidad Anderson-Darling
from scipy.stats import levene ## Prueba homogeneidad de varianzas Levene
from scipy.stats import mannwhitneyu ## Prueba Mann-Whitney-Wilcoxon (comparación dos poblaciones)
from scipy.stats import f_oneway ## Prueba ANOVA de una vía
from scipy.stats import chi2_contingency ## Prueba chi cuadrado de Pearson
from scipy.stats import pearsonr ## Coeficiente de correlación de Pearson con prueba
import statsmodels.stats as sm ## estadísticas
import statsmodels.api as sm1 ## estadísticas
from statsmodels.graphics.gofplots import qqplot ## Gráfico QQ plot
La pandemia de COVID-19 ha sido un reto sin precedentes para la salud pública global. A nivel mundial se han reportado “más de 767 millones de casos confirmados y más de 6,9 millones de muertes” hasta junio de 2023. La región de las Américas ha sido una de las más golpeadas con el 43% de las defunciones reportadas globalmente y más de 2.9 millones de defunciones hasta dicha fecha.
La Organización Mundial de la Salud (OMS) ha resaltado la necesidad de contar con estudios post introducción de las vacunas contra COVID-19 en condiciones reales que capten las diferencias en la epidemiología de la enfermedad a nivel subnacional y en las variaciones programáticas de la implementación de las estrategias y políticas de vacunación.
En Colombia la vacunación contra COVID-19 inició en febrero de 2021 dirigida inicialmente a grupos de riesgo y paulatinamente a la población general.
Se tomo una muestra del conjunto de datos original, esta muestra contiene la información de 500.000 individuos de 4 ciudades de Colombia que recibieron al menos una dosis de la vacuna contra COVID-19, además se incluye información del sexo, la edad, el municipio en donde se vacuno, las fechas de cuando se aplicó la vacuna, también si la persona tiene alguna comorbilidad e información referente a los 3 desenlaces que se evalúan (confirmado, hospitalizado, fallecido).
¿Existen diferencias significativas entre los individuos vacunados contra COVID-19 en 4 ciudades colombianas durante el periodo de 2021 a 2022 y sus desenlaces?
Specific: Se quiere saber si existen diferencias entre las personas que se vacunaron en las 4 ciudades colombianas.
Measurable: Se mide con pruebas estadísticas.
Action – oriented: Alcanzable con la información disponible y se motiva a realizar la investigación en otras ciudades o países.
Relevant: Se contribuye a la investigación con relación a las vacunas del COVID.
Time – bound: Se pretende estudiar en un plazo de 1 año.
evaluar si existen diferencias significativas en la base de datos que nos puedan indicar cómo se comportan los individuos vacunados teniendo en cuenta las diferentes variables que se incluyen en la bd
Cada fila contiene la información de una persona vacunada, esta puede aparecer hasta máximo 4 veces. Esto se debe a que la persona puede tener aplicadas 4 distintas dosis (primera dosis, segunda dosis, primer refuerzo y segundo refuerzo), a continuación, se describen las variables del dataset:
Empezaremos por revisar la estructura del dataset, la consistencia entre los datos y que tengan el formato adecuado.
#Seccion 3.1
url = "https://media.githubusercontent.com/media/carlosjara/PRY_EDA_ICESI/main/data/Muestra.csv"
df = pd.read_csv(url, sep="|")
df.shape
(1134483, 23)
Los datos cuentan con 1134483 observaciones (de 500000 personas - que pueden tener mas de una dosis) y 23 columnas
df.head()
| PersonaBasicaID | Sexo | Edad | FechaNacimiento | DepartamentoAplicacion | MunicipioAplicacion | Biológico | FechaApliacion | NumDosis | CAC_VIH | ... | CAC_Cancer | CAC_Artritis | Confirmado | FechaInicioSintomas | ServicioMayorComplejidad | FechaIngresoServicioMayorComplejidad | NDEstadoVital | NDFechaDefuncion | EstadoAfiliacion | Regimen | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 11638609 | MASCULINO | 39 | 19,840,118.00 | 05 - Antioquia | 05001 - Medellín | JANSSEN | 20220212 | 0 | 0 | ... | 0 | 0 | 1 | 20210618 | Ninguno | 19000101 | VIVO | 19000101 | ACTIVO | CONTRIBUTIVO |
| 1 | 29692489 | FEMENINO | 89 | 19,331,110.00 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 20220224 | -1 | 0 | ... | 0 | 0 | 1 | 19000101 | Ninguno | 20210428 | VIVO | 19000101 | ACTIVO | CONTRIBUTIVO |
| 2 | 29692489 | FEMENINO | 89 | 19,331,110.00 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 20210707 | 2 | 0 | ... | 0 | 0 | 1 | 19000101 | Ninguno | 20210428 | VIVO | 19000101 | ACTIVO | CONTRIBUTIVO |
| 3 | 29692489 | FEMENINO | 89 | 19,331,110.00 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 20210314 | 1 | 0 | ... | 0 | 0 | 1 | 19000101 | Ninguno | 20210428 | VIVO | 19000101 | ACTIVO | CONTRIBUTIVO |
| 4 | 8402648 | FEMENINO | 75 | 19,470,920.00 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 20210625 | 1 | 0 | ... | 0 | 0 | 0 | 19000101 | Ninguno | 19000101 | VIVO | 19000101 | ACTIVO | SUBSIDIADO |
5 rows × 23 columns
# Seccion 3.1.1
#Vamos dejar el mismor formato de fecha para las columnas correspondientes
columnas = ['FechaNacimiento','FechaInicioSintomas','FechaApliacion','FechaIngresoServicioMayorComplejidad','NDFechaDefuncion']
#usaremos la funcion to_datetime para dar un formato estandarizado
class FechaTransformer:
def __init__(self, date_columns):
self.date_columns = date_columns
def transform_dates(self, df):
for col in self.date_columns:
df[col] = pd.to_datetime(df[col], format='%Y%m%d', errors='coerce')
return df
columnas = ['FechaNacimiento','FechaInicioSintomas','FechaApliacion','FechaIngresoServicioMayorComplejidad','NDFechaDefuncion']
transformer = FechaTransformer(columnas)
df = transformer.transform_dates(df)
Ver Codigo sin usar clase transform
df.head()
| PersonaBasicaID | Sexo | Edad | FechaNacimiento | DepartamentoAplicacion | MunicipioAplicacion | Biológico | FechaApliacion | NumDosis | CAC_VIH | ... | CAC_Cancer | CAC_Artritis | Confirmado | FechaInicioSintomas | ServicioMayorComplejidad | FechaIngresoServicioMayorComplejidad | NDEstadoVital | NDFechaDefuncion | EstadoAfiliacion | Regimen | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 11638609 | MASCULINO | 39 | 1984-01-18 | 05 - Antioquia | 05001 - Medellín | JANSSEN | 2022-02-12 | 0 | 0 | ... | 0 | 0 | 1 | 2021-06-18 | Ninguno | 1900-01-01 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO |
| 1 | 29692489 | FEMENINO | 89 | 1933-11-10 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2022-02-24 | -1 | 0 | ... | 0 | 0 | 1 | 1900-01-01 | Ninguno | 2021-04-28 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO |
| 2 | 29692489 | FEMENINO | 89 | 1933-11-10 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2021-07-07 | 2 | 0 | ... | 0 | 0 | 1 | 1900-01-01 | Ninguno | 2021-04-28 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO |
| 3 | 29692489 | FEMENINO | 89 | 1933-11-10 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2021-03-14 | 1 | 0 | ... | 0 | 0 | 1 | 1900-01-01 | Ninguno | 2021-04-28 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO |
| 4 | 8402648 | FEMENINO | 75 | 1947-09-20 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2021-06-25 | 1 | 0 | ... | 0 | 0 | 0 | 1900-01-01 | Ninguno | 1900-01-01 | VIVO | 1900-01-01 | ACTIVO | SUBSIDIADO |
5 rows × 23 columns
# Fin seccion 3.1.1
# Seccion 3.1.2
# importante mencionar que en secciones posteriores se hará el tratamiento de los datos
Value_count = df.isna().sum()
Value_Count_Mayor = Value_count[Value_count > 0]
print(f'Resumen:\r\n',Value_Count_Mayor)
Resumen: Sexo 522 FechaNacimiento 68 dtype: int64
Se puede ver que hay datos vacíos, para la variable FechaNacimiento y por tanto Edad de aplicacion, para la variable Sexo. En el proceso de limpieza se decidirá qué hacer con estas observaciones.
df['FechaNacimiento'].isna().sum()
68
#Vamos a calcular la fecha promeido de nacimiento, para imputar
fecha_promedio = pd.to_datetime(df['FechaNacimiento'], errors='coerce').mean()
df['FechaNacimiento'].fillna(fecha_promedio, inplace=True)
#Calculamos la edad teniendo en cuenta la fecha de aplicacion
df['EdadAplicacion'] = (df['FechaApliacion'] - df['FechaNacimiento']).astype('<m8[Y]').astype('int64')
df.head()
| PersonaBasicaID | Sexo | Edad | FechaNacimiento | DepartamentoAplicacion | MunicipioAplicacion | Biológico | FechaApliacion | NumDosis | CAC_VIH | ... | CAC_Artritis | Confirmado | FechaInicioSintomas | ServicioMayorComplejidad | FechaIngresoServicioMayorComplejidad | NDEstadoVital | NDFechaDefuncion | EstadoAfiliacion | Regimen | EdadAplicacion | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 11638609 | MASCULINO | 39 | 1984-01-18 | 05 - Antioquia | 05001 - Medellín | JANSSEN | 2022-02-12 | 0 | 0 | ... | 0 | 1 | 2021-06-18 | Ninguno | 1900-01-01 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO | 38 |
| 1 | 29692489 | FEMENINO | 89 | 1933-11-10 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2022-02-24 | -1 | 0 | ... | 0 | 1 | 1900-01-01 | Ninguno | 2021-04-28 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO | 88 |
| 2 | 29692489 | FEMENINO | 89 | 1933-11-10 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2021-07-07 | 2 | 0 | ... | 0 | 1 | 1900-01-01 | Ninguno | 2021-04-28 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO | 87 |
| 3 | 29692489 | FEMENINO | 89 | 1933-11-10 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2021-03-14 | 1 | 0 | ... | 0 | 1 | 1900-01-01 | Ninguno | 2021-04-28 | VIVO | 1900-01-01 | ACTIVO | CONTRIBUTIVO | 87 |
| 4 | 8402648 | FEMENINO | 75 | 1947-09-20 | 11 - Bogotá, D.C. | 11001 - Bogotá, D.C. | SINOVAC | 2021-06-25 | 1 | 0 | ... | 0 | 0 | 1900-01-01 | Ninguno | 1900-01-01 | VIVO | 1900-01-01 | ACTIVO | SUBSIDIADO | 73 |
5 rows × 24 columns
La estructura de las columnas de fecha, se estandarizan para un mismo formato, teniendo en cuenta los nulos en la fecha de aplicacion, se actualiza la columna Edad por Edad de aplicacion que toma el valor promedio de nacimiento para imputar.
fig, ax = plt.subplots(figsize=(6,4))
# Create boxplots for 'EdadAplicacion' and 'EdadCalculada'
boxplot1 = ax.boxplot(df['EdadAplicacion'], positions=[1], patch_artist=True, boxprops=dict(facecolor='blue'))
boxplot2 = ax.boxplot(df['Edad'], positions=[2], patch_artist=True, boxprops=dict(facecolor='red'))
# Customize labels for the boxplots
ax.set_xticks([1, 2])
ax.set_xticklabels(['Edad Calculada', 'Edad Cargada'])
# Add legend manually
legend_labels = ['Edad Calculada', 'Edad Cargada']
colors = ['blue', 'red']
legend_patches = [plt.Rectangle((0, 0), 1, 1, color=color) for color in colors]
ax.legend(legend_patches, legend_labels)
# Add labels and title
ax.set_xlabel('Edad')
ax.set_title('Edad de cargada y Edad Calculada')
# Show the plot
plt.show()
df['EdadAplicacion'].describe()
count 1,134,483.00 mean 43.49 std 19.99 min 1.00 25% 28.00 50% 42.00 75% 59.00 max 121.00 Name: EdadAplicacion, dtype: float64
# Crear los grupos de edad
bins = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110]
labels = ['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70-79', '80-89', '90-99', '100+']
df['GrupoEdad'] = pd.cut(df['EdadAplicacion'], bins=bins, labels=labels, right=False)
# Contar la cantidad de observaciones en cada grupo de edad
edad_counts = df['GrupoEdad'].value_counts().sort_index()
# Generar un gráfico de barras
plt.figure(figsize=(10, 6))
edad_counts.plot(kind='bar', color='skyblue')
plt.title('Distribución de Edades en Grupos de 10 Años')
plt.xlabel('Grupo de Edad')
plt.ylabel('Cantidad de Personas')
plt.xticks(rotation=45)
plt.show()
df['Sexo'].isna().sum()
# sera necesario hacer label enconding para imputacion con knn
522
(df.value_counts('Sexo',normalize=True)*100).round(2).astype(str)+'%'
Sexo FEMENINO 54.51% MASCULINO 45.46% NO DEFINIDO 0.03% INDEFINIDO 0.0% dtype: object
df_form = df['Sexo'].value_counts(normalize=True).rename('Proportion').reset_index()
df_form.rename(columns={'index':'Sexo','Proportion':'Porcentaje'},inplace=True)
df_form.Porcentaje=np.round(df_form.Porcentaje * 100,decimals=2)
df_form.Sexo = df_form.Sexo
sns.set(style="whitegrid")
plt.figure(figsize=(8, 6))
ax = sns.barplot(x='Sexo', y='Porcentaje', data=df_form, palette='viridis')
plt.title('Distribución de Sexo')
plt.xlabel('Sexo')
plt.ylabel('Porcentaje')
plt.ylim(0, 70)
for p in ax.patches:
ax.annotate(f'{p.get_height():.2f}%', (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', fontsize=12, color='black', xytext=(0, 5), textcoords='offset points')
plt.show()
Value_count = df.isna().sum()
Value_Count_Mayor = Value_count[Value_count > 0]
print(f'Resumen:\r\n',Value_Count_Mayor)
Resumen: Sexo 522 GrupoEdad 3 dtype: int64
df['Sexo'].unique()
array(['MASCULINO', 'FEMENINO', nan, 'NO DEFINIDO', 'INDEFINIDO'],
dtype=object)
# Fin seccion 3.1.2
#Seccion 3.1.3
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1134483 entries, 0 to 1134482 Data columns (total 25 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 PersonaBasicaID 1134483 non-null int64 1 Sexo 1133961 non-null object 2 Edad 1134483 non-null int64 3 FechaNacimiento 1134483 non-null datetime64[ns] 4 DepartamentoAplicacion 1134483 non-null object 5 MunicipioAplicacion 1134483 non-null object 6 Biológico 1134483 non-null object 7 FechaApliacion 1134483 non-null datetime64[ns] 8 NumDosis 1134483 non-null int64 9 CAC_VIH 1134483 non-null int64 10 CAC_HTA 1134483 non-null int64 11 CAC_Diabetes 1134483 non-null int64 12 CAC_PEH 1134483 non-null int64 13 CAC_Cancer 1134483 non-null int64 14 CAC_Artritis 1134483 non-null int64 15 Confirmado 1134483 non-null int64 16 FechaInicioSintomas 1134483 non-null datetime64[ns] 17 ServicioMayorComplejidad 1134483 non-null object 18 FechaIngresoServicioMayorComplejidad 1134483 non-null datetime64[ns] 19 NDEstadoVital 1134483 non-null object 20 NDFechaDefuncion 1134483 non-null datetime64[ns] 21 EstadoAfiliacion 1134483 non-null object 22 Regimen 1134483 non-null object 23 EdadAplicacion 1134483 non-null int64 24 GrupoEdad 1134480 non-null category dtypes: category(1), datetime64[ns](5), int64(11), object(8) memory usage: 208.8+ MB
#df['CAC_Artritis'] = df['CAC_Artritis'].astype('object')
#df['CAC_VIH'] = df['CAC_VIH'].astype('object')
#df['CAC_HTA'] = df['CAC_HTA'].astype('object')
#df['CAC_Diabetes'] = df['CAC_Diabetes'].astype('object')
#df['CAC_PEH'] = df['CAC_PEH'].astype('object')
#df['CAC_Cancer'] = df['CAC_Cancer'].astype('object')
en esta seccion podemos corregir valores por su estructura y calcular valores basados en reemplazo de fechas por su media (Edad de aplicacion), en secciones posteriores se hará el tratamiento a demas variables
# Fin seccion 3.1.3
Se busca mantener estructura de campos normalizado para uso adecuado de tipos y busquedas, en este caso las columnas DepartamentoAplicacion y MunicipioAplicacion pueden ser dividios.
df[['CodigoDepartamento', 'DescripcionDepartamento']] = df['DepartamentoAplicacion'].str.split(' - ', 1, expand=True)
df[['CodigoMunicipio', 'DescripcionMunicipio']] = df['MunicipioAplicacion'].str.split(' - ', 1, expand=True)
#df.drop(columns=['DepartamentoAplicacion', 'MunicipioAplicacion'], inplace=True)
columnas_numericas = ['CodigoDepartamento', 'CodigoMunicipio']
df[columnas_numericas] = df[columnas_numericas].astype('int64')
df.dtypes
PersonaBasicaID int64 Sexo object Edad int64 FechaNacimiento datetime64[ns] DepartamentoAplicacion object MunicipioAplicacion object Biológico object FechaApliacion datetime64[ns] NumDosis int64 CAC_VIH int64 CAC_HTA int64 CAC_Diabetes int64 CAC_PEH int64 CAC_Cancer int64 CAC_Artritis int64 Confirmado int64 FechaInicioSintomas datetime64[ns] ServicioMayorComplejidad object FechaIngresoServicioMayorComplejidad datetime64[ns] NDEstadoVital object NDFechaDefuncion datetime64[ns] EstadoAfiliacion object Regimen object EdadAplicacion int64 GrupoEdad category CodigoDepartamento int64 DescripcionDepartamento object CodigoMunicipio int64 DescripcionMunicipio object dtype: object
#seccion 3.1.4
# Fin seccion 3.1.4
# Fin seccion 3.1
Según la descripción de los campos, las siguientes variables son categoricas: Sexo, DepartamentoAplicacion, MunicipioAplicacion, Biológico, NumDosis, CAC_VIH, CAC_HTA, CAC_Diabetes, CAC_PEH, CAC_Cancer, CAC_Artritis, CAC_Artritis, Confirmado, ServicioMayorComplejidad, NDEstadoVital, EstadoAfiliacion, Regimen.
Vamos a ver con qué tipo de dato se encuentran actualmente:
Análisis de estructura de campos y Limpieza de datos
df1 = df[['PersonaBasicaID', 'Sexo', 'Edad', 'NumDosis', 'CAC_VIH', 'CAC_HTA', 'CAC_Diabetes',
'CAC_PEH', 'CAC_Cancer', 'CAC_Artritis', 'Confirmado', 'ServicioMayorComplejidad', 'NDEstadoVital','FechaNacimiento']]
df['ServicioMayorComplejidad'] = np.where(
df['FechaIngresoServicioMayorComplejidad'] == pd.Timestamp('1900-01-01'),
'No' , 'Hospitalizado')
df1["Confirmado"] = np.where(df1['Confirmado']== 'Positivo',1,0)
df1["Confirmado"]= df1["Confirmado"].astype('float64')
df1["NDEstadoVital"] = np.where(df1['NDEstadoVital']== 'VIVO',0,1)
df1["NDEstadoVital"]= df1["NDEstadoVital"].astype('float64')
df1["ServicioMayorComplejidad"] = np.where(df1['ServicioMayorComplejidad']== 'Hospitalizado',1,0)
df1["ServicioMayorComplejidad"]= df1["ServicioMayorComplejidad"].astype('float64')
correlation_mat = df1.corr()
mask = np.zeros_like(correlation_mat)
mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
f, ax = plt.subplots(figsize=(13,6))
ax = sns.heatmap(correlation_mat,
mask=mask,annot=True)
# Sacamos las personas unicas
df_unicos = df.drop_duplicates(subset=['PersonaBasicaID'])
tabla_freq=(pd.crosstab(index=df_unicos["Sexo"],columns="count")).reset_index()
tabla_freq['Freq. Rel.']=tabla_freq['count']/sum(tabla_freq['count'])
tabla_freq.rename(columns={'count':'Freq. Abs.'},inplace=True)
tabla_freq=tabla_freq.sort_values(by='Freq. Abs.',ascending=False).reset_index(drop=True)
tabla_freq
| col_0 | Sexo | Freq. Abs. | Freq. Rel. |
|---|---|---|---|
| 0 | FEMENINO | 268531 | 0.54 |
| 1 | MASCULINO | 231003 | 0.46 |
| 2 | NO DEFINIDO | 202 | 0.00 |
| 3 | INDEFINIDO | 1 | 0.00 |
fig_cont_fr = plt.figure()
ax = fig_cont_fr.add_axes([0,0,1,1])
ax.bar(tabla_freq['Sexo'],tabla_freq['Freq. Rel.']*100,color='lightskyblue')
ax.bar_label(ax.containers[0], label_type='edge',fmt='%.2f%%')
ax.yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
ax.set(ylim=(0, 65))
plt.title("Distribucion del sexo de las personas vacunadas por Municipio")
plt.ylabel('Frecuencia Relativa')
plt.xlabel('Sexo')
plt.show()
tabla_freq=(pd.crosstab(index=df_unicos["DescripcionMunicipio"],columns="count")).reset_index()
tabla_freq['Freq. Rel.']=tabla_freq['count']/sum(tabla_freq['count'])
tabla_freq.rename(columns={'count':'Freq. Abs.'},inplace=True)
tabla_freq=tabla_freq.sort_values(by='Freq. Abs.',ascending=False).reset_index(drop=True)
tabla_freq
| col_0 | DescripcionMunicipio | Freq. Abs. | Freq. Rel. |
|---|---|---|---|
| 0 | Bogotá, D.C. | 297325 | 0.59 |
| 1 | Medellín | 108631 | 0.22 |
| 2 | Cali | 77804 | 0.16 |
| 3 | Montería | 16240 | 0.03 |
fig_torta = plt.figure()
ax = fig_torta.add_axes([0,0,1,1])
plt.pie(tabla_freq["Freq. Rel."], labels=tabla_freq["DescripcionMunicipio"],autopct='%1.1f%%',startangle=90)
plt.show()
En este caso vamos a estandarizar los nombres de los biológicos para no tener tantas categorias.
Ver Codigo sin usar clase transform
#Transformador personalizado que extrae columnas pasadas como argumento a su constructor
class BiologicoTransformer():
#Class Constructor
def __init__(self, feature_names):
self._feature_names = feature_names
#Return self nada más que hacer aquí
def fit(self, X, y = None):
return self
#Método que describe lo que necesitamos que haga este transformador
def transform(self, X, y = None ):
def map_values(val):
if val in ['SINOVAC PARTICULAR']:
return 'SINOVAC'
elif val in ['PFIZER PARTICULAR']:
return 'PFIZER'
elif val in ['JANSSEN PARTICULAR']:
return 'JANSSEN'
elif val in ['MODERNA PARTICULAR']:
return 'MODERNA'
elif val in ['ASTRAZENECA PARTICULAR']:
return 'ASTRAZENECA'
else:
return val
X_ = X.copy()
X_[self._feature_names] = X_[self._feature_names].apply(map_values)
return X_
pipeline1 = Pipeline(steps=[("CT", BiologicoTransformer("Biológico"))])
result = pipeline1.fit_transform(df)
result['Biológico'].value_counts()
PFIZER 387662 SINOVAC 301491 MODERNA 181126 ASTRAZENECA 178781 JANSSEN 85423 Name: Biológico, dtype: int64
tabla_freq=(pd.crosstab(index=result["Biológico"],columns="count")).reset_index()
tabla_freq['Freq. Rel.']=tabla_freq['count']/sum(tabla_freq['count'])
tabla_freq.rename(columns={'count':'Freq. Abs.'},inplace=True)
tabla_freq=tabla_freq.sort_values(by='Freq. Abs.',ascending=False).reset_index(drop=True)
tabla_freq
| col_0 | Biológico | Freq. Abs. | Freq. Rel. |
|---|---|---|---|
| 0 | PFIZER | 387662 | 0.34 |
| 1 | SINOVAC | 301491 | 0.27 |
| 2 | MODERNA | 181126 | 0.16 |
| 3 | ASTRAZENECA | 178781 | 0.16 |
| 4 | JANSSEN | 85423 | 0.08 |
fig_cont_fr = plt.figure()
ax = fig_cont_fr.add_axes([0,0,1,1])
ax.bar(tabla_freq['Biológico'],tabla_freq['Freq. Rel.']*100,color='lightskyblue')
ax.bar_label(ax.containers[0], label_type='edge',fmt='%.2f%%')
ax.yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
ax.set(ylim=(0, 40))
plt.title("Distribucion de los biológicos aplicados")
plt.ylabel('Frecuencia Relativa')
plt.xlabel('Biológico')
plt.show()
df_revision_biologico = result
sns.set(style="whitegrid") # Configuración opcional de estilo
sns.countplot(x="Biológico", hue='Confirmado', data=df_revision_biologico)
<Axes: xlabel='Biológico', ylabel='count'>
Al igual como se hizo con la variable Biológico, vamos a cambiar los nombres de las dosis para entender cuales son.
Ver Codigo sin usar clase transform
#Transformador personalizado que extrae columnas pasadas como argumento a su constructor
class DosisTransformer():
#Class Constructor
def __init__(self, feature_names):
self._feature_names = feature_names
#Return self nada más que hacer aquí
def fit(self, X, y = None):
return self
#Método que describe lo que necesitamos que haga este transformador
def transform(self, X, y = None ):
def map_values(val):
if val == 1:
return 'Primera Dosis'
elif val == 2:
return 'Segunda Dosis'
elif val == 0:
return 'Unica Dosis'
elif val == -1:
return 'Primer Refuerzo'
elif val == -2:
return 'Segundo Refuerzo'
else:
return val
X_ = X.copy()
X_[self._feature_names] = X_[self._feature_names].apply(map_values)
return X_
pipeline1 = Pipeline(steps=[("CT", DosisTransformer("NumDosis"))])
result = pipeline1.fit_transform(df)
result['NumDosis'].value_counts()
Primera Dosis 440493 Segunda Dosis 388224 Primer Refuerzo 204079 Unica Dosis 63872 Segundo Refuerzo 37815 Name: NumDosis, dtype: int64
tabla_freq=(pd.crosstab(index=result["NumDosis"],columns="count")).reset_index()
tabla_freq['Freq. Rel.']=tabla_freq['count']/sum(tabla_freq['count'])
tabla_freq.rename(columns={'count':'Freq. Abs.'},inplace=True)
tabla_freq=tabla_freq.sort_values(by='Freq. Abs.',ascending=False).reset_index(drop=True)
tabla_freq
| col_0 | NumDosis | Freq. Abs. | Freq. Rel. |
|---|---|---|---|
| 0 | Primera Dosis | 440493 | 0.39 |
| 1 | Segunda Dosis | 388224 | 0.34 |
| 2 | Primer Refuerzo | 204079 | 0.18 |
| 3 | Unica Dosis | 63872 | 0.06 |
| 4 | Segundo Refuerzo | 37815 | 0.03 |
fig_cont_fr = plt.figure()
ax = fig_cont_fr.add_axes([0,0,1,1])
ax.bar(tabla_freq['NumDosis'],tabla_freq['Freq. Rel.']*100,color='lightskyblue')
ax.bar_label(ax.containers[0], label_type='edge',fmt='%.2f%%')
ax.yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
ax.set(ylim=(0, 45))
plt.title("Distribucion de las dosis aplicadas")
plt.ylabel('Frecuencia Relativa')
plt.xlabel('Dosis')
plt.show()
df_revision = result
order = ['Primera Dosis', 'Segunda Dosis', 'Unica Dosis','Primer Refuerzo','Segundo Refuerzo'] # Reemplaza con tus categorías reales
sns.set(style="whitegrid") # Configuración opcional de estilo
sns.countplot(x="NumDosis", hue='Confirmado', data=result,order=order)
<Axes: xlabel='NumDosis', ylabel='count'>
# se puede ver que aunque se tienen hasta segunda dosis o refuerzos, se siguen presentando casos confirmados, con tendencia hacia la baja
Variables que nos indican las comorbilidades
tabla_freq1=(pd.crosstab(index=df_unicos["CAC_VIH"],columns="count")).reset_index()
tabla_freq1['Freq. Rel.']=tabla_freq1['count']/sum(tabla_freq1['count'])
tabla_freq2=(pd.crosstab(index=df_unicos["CAC_HTA"],columns="count")).reset_index()
tabla_freq2['Freq. Rel.']=tabla_freq2['count']/sum(tabla_freq1['count'])
tabla_freq3=(pd.crosstab(index=df_unicos["CAC_Diabetes"],columns="count")).reset_index()
tabla_freq3['Freq. Rel.']=tabla_freq3['count']/sum(tabla_freq1['count'])
tabla_freq4=(pd.crosstab(index=df_unicos["CAC_PEH"],columns="count")).reset_index()
tabla_freq4['Freq. Rel.']=tabla_freq4['count']/sum(tabla_freq1['count'])
tabla_freq5=(pd.crosstab(index=df_unicos["CAC_Cancer"],columns="count")).reset_index()
tabla_freq5['Freq. Rel.']=tabla_freq5['count']/sum(tabla_freq1['count'])
tabla_freq6=(pd.crosstab(index=df_unicos["CAC_Artritis"],columns="count")).reset_index()
tabla_freq6['Freq. Rel.']=tabla_freq6['count']/sum(tabla_freq1['count'])
fig, axs = plt.subplots(2, 3, figsize=(14, 10))
# fig.set_size_inches(15, 5)
fig.subplots_adjust(wspace=0.3)
barr1 = axs[0][0].bar(tabla_freq1["CAC_VIH"],tabla_freq1['Freq. Rel.']*100,color='lightskyblue')
axs[0][0].bar_label(barr1,label_type='edge',fmt='%.2f%%')
axs[0][0].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[0][0].set(ylim=(0, 110))
axs[0][0].set_title('VIH')
barr2 = axs[0][1].bar(tabla_freq2["CAC_HTA"],tabla_freq2['Freq. Rel.']*100,color='lightskyblue')
axs[0][1].bar_label(barr2, label_type='edge',fmt='%.2f%%')
axs[0][1].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[0][1].set(ylim=(0, 110))
axs[0][1].set_title('Hipertensión Arterial')
barr3 = axs[0][2].bar(tabla_freq3["CAC_Diabetes"],tabla_freq3['Freq. Rel.']*100,color='lightskyblue')
axs[0][2].bar_label(barr3, label_type='edge',fmt='%.2f%%')
axs[0][2].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[0][2].set(ylim=(0, 110))
axs[0][2].set_title('Diabetes')
barr4 = axs[1][0].bar(tabla_freq4["CAC_PEH"],tabla_freq4['Freq. Rel.']*100,color='lightskyblue')
axs[1][0].bar_label(barr4, label_type='edge',fmt='%.2f%%')
axs[1][0].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[1][0].set(ylim=(0, 110))
axs[1][0].set_title('Enfermedades Huerfanas')
barr5 = axs[1][1].bar(tabla_freq5["CAC_Cancer"],tabla_freq5['Freq. Rel.']*100,color='lightskyblue')
axs[1][1].bar_label(barr5, label_type='edge',fmt='%.2f%%')
axs[1][1].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[1][1].set(ylim=(0, 110))
axs[1][1].set_title('Cáncer')
barr6 = axs[1][2].bar(tabla_freq6["CAC_Artritis"],tabla_freq6['Freq. Rel.']*100,color='lightskyblue')
axs[1][2].bar_label(barr6, label_type='edge',fmt='%.2f%%')
axs[1][2].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[1][2].set(ylim=(0, 110))
axs[1][2].set_title('Artritis')
plt.show()
df_revision['NumDosis'].value_counts()
Primera Dosis 440493 Segunda Dosis 388224 Primer Refuerzo 204079 Unica Dosis 63872 Segundo Refuerzo 37815 Name: NumDosis, dtype: int64
df_revision['Confirmado'].value_counts()
0 864946 1 269537 Name: Confirmado, dtype: int64
#datos de solo la segunda dosis y casos confirmados
df_segunda_dosis_confirmados = df_revision[(df_revision['NumDosis'] == 'Segunda Dosis') & (df_revision['Confirmado'] == 1)]
# comorbilidades
caracteristicas = ["CAC_VIH", "CAC_HTA", "CAC_Diabetes", "CAC_PEH", "CAC_Cancer", "CAC_Artritis"]
resultados = pd.DataFrame()
# Calcula el número de casos confirmados para cada característica
for caracteristica in caracteristicas:
casos_confirmados = df_segunda_dosis_confirmados[df_segunda_dosis_confirmados[caracteristica] == 1].shape[0]
resultados.loc[0, caracteristica] = casos_confirmados
sns.set(rc={'figure.figsize': (10, 6)})
resultados = resultados.T
resultados.plot(kind='bar', stacked=True)
plt.title("Características vs. Confirmados (Segunda Dosis)")
plt.xlabel("Características de Comorbilidad")
plt.ylabel("Número de Confirmados")
plt.legend(title="Confirmado", labels=["Sí"])
plt.show()
Los pacientes con HTA y Diabetes son aquellos que aun vacunados han tenido casos de reinfeccion aun teniendo una segunda dosis.
Variables referentes a los descenlaces clinicos
df_unicos['Confirmado'] = np.where(df_unicos['Confirmado'] == 1, 'Positivo' , 'No')
df['Confirmado'] = np.where(df['Confirmado'] == 1, 'Positivo' , 'No')
df_unicos['Confirmado'].value_counts()
No 390984 Positivo 109016 Name: Confirmado, dtype: int64
df_unicos['ServicioMayorComplejidad'] = np.where(df_unicos['FechaIngresoServicioMayorComplejidad'] == pd.Timestamp('1900-01-01'), 'No' , 'Hospitalizado')
df['ServicioMayorComplejidad'] = np.where(df['FechaIngresoServicioMayorComplejidad'] == pd.Timestamp('1900-01-01'), 'No' , 'Hospitalizado')
df_unicos['ServicioMayorComplejidad'].value_counts()
No 444603 Hospitalizado 55397 Name: ServicioMayorComplejidad, dtype: int64
tabla_freq1=(pd.crosstab(index=df_unicos["Confirmado"],columns="count")).reset_index()
tabla_freq1['Freq. Rel.']=tabla_freq1['count']/sum(tabla_freq1['count'])
tabla_freq2=(pd.crosstab(index=df_unicos["ServicioMayorComplejidad"],columns="count")).reset_index()
tabla_freq2['Freq. Rel.']=tabla_freq2['count']/sum(tabla_freq1['count'])
tabla_freq3=(pd.crosstab(index=df_unicos["NDEstadoVital"],columns="count")).reset_index()
tabla_freq3['Freq. Rel.']=tabla_freq3['count']/sum(tabla_freq1['count'])
fig, axs = plt.subplots(1, 3, figsize=(14, 4))
# fig.set_size_inches(15, 5)
fig.subplots_adjust(wspace=0.3)
barr1 = axs[0].bar(tabla_freq1["Confirmado"],tabla_freq1['Freq. Rel.']*100,color='lightskyblue')
axs[0].bar_label(barr1,label_type='edge',fmt='%.2f%%')
axs[0].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[0].set(ylim=(0, 110))
axs[0].set_title('Confirmado')
barr2 = axs[1].bar(tabla_freq2["ServicioMayorComplejidad"],tabla_freq2['Freq. Rel.']*100,color='lightskyblue')
axs[1].bar_label(barr2, label_type='edge',fmt='%.2f%%')
axs[1].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[1].set(ylim=(0, 110))
axs[1].set_title('Hospitalización')
barr3 = axs[2].bar(tabla_freq3["NDEstadoVital"],tabla_freq3['Freq. Rel.']*100,color='lightskyblue')
axs[2].bar_label(barr3, label_type='edge',fmt='%.2f%%')
axs[2].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[2].set(ylim=(0, 110))
axs[2].set_title('Muerte')
plt.show()
Variables estado de afiliación y regimen
tabla_freq1=(pd.crosstab(index=df_unicos["EstadoAfiliacion"],columns="count")).reset_index()
tabla_freq1['Freq. Rel.']=tabla_freq1['count']/sum(tabla_freq1['count'])
tabla_freq2=(pd.crosstab(index=df_unicos["Regimen"],columns="count")).reset_index()
tabla_freq2['Freq. Rel.']=tabla_freq2['count']/sum(tabla_freq1['count'])
fig, axs = plt.subplots(1, 2, figsize=(14, 4))
# fig.set_size_inches(15, 5)
fig.subplots_adjust(wspace=0.3)
barr1 = axs[0].bar(tabla_freq1["EstadoAfiliacion"],tabla_freq1['Freq. Rel.']*100,color='lightskyblue')
axs[0].bar_label(barr1,label_type='edge',fmt='%.2f%%',)
axs[0].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[0].set(ylim=(0, 110))
axs[0].set_title('Estado de Afiliacion')
axs[0].tick_params(axis='x', rotation=45)
barr2 = axs[1].bar(tabla_freq2["Regimen"],tabla_freq2['Freq. Rel.']*100,color='lightskyblue')
axs[1].bar_label(barr2, label_type='edge',fmt='%.2f%%')
axs[1].yaxis.set_major_formatter(mtick.PercentFormatter(xmax=100))
axs[1].set(ylim=(0, 110))
axs[1].set_title('Régimen')
plt.show()
Descripcion de outliers (Valores atipicos) dentro del conjunto de datos, es decir, que esten significativamente alejandos del rango esperado de valores.
#Validamos las caracteristicas estadisticas de las columnas numericas para detrminar valores minimos (negativos) o maximos (positivos)
df.select_dtypes(include=['number']).describe()
| PersonaBasicaID | Edad | NumDosis | CAC_VIH | CAC_HTA | CAC_Diabetes | CAC_PEH | CAC_Cancer | CAC_Artritis | EdadAplicacion | CodigoDepartamento | CodigoMunicipio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 | 1,134,483.00 |
| mean | 55,942,567.09 | 44.91 | 0.83 | 0.00 | 0.15 | 0.05 | 0.00 | 0.01 | 0.00 | 43.49 | 19.75 | 19,748.54 |
| std | 43,540,300.43 | 20.12 | 1.18 | 0.07 | 0.35 | 0.21 | 0.04 | 0.10 | 0.06 | 19.99 | 23.63 | 23,630.65 |
| min | 2.00 | 2.00 | -2.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 1.00 | 5.00 | 5,001.00 |
| 25% | 20,773,274.50 | 29.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 28.00 | 11.00 | 11,001.00 |
| 50% | 41,547,199.00 | 44.00 | 1.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 42.00 | 11.00 | 11,001.00 |
| 75% | 100,005,058.00 | 60.00 | 2.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 59.00 | 11.00 | 11,001.00 |
| max | 146,087,548.00 | 198.00 | 2.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 121.00 | 76.00 | 76,001.00 |
Se puede ver que hay valores atipicos en edad y edadaplicacion (columna calculada con la fecha de nacimiento promedio y la fecha de aplicacion del biologico, 198 cuando la media es 44 y 121 cuando la media es 49, para el caso de los codigos de departamento ,municipio y personabasicaid no se consideran para el analisis, dada su naturaleza
#graficamos
fig = px.histogram(df,x='Edad',nbins=30)
fig.show()
print(f"Skewness: {df['Edad'].skew()}")
print(f"Kurtosis: {df['Edad'].kurt()}")
Skewness: 0.19300793224563942 Kurtosis: -0.5401157740589331
# Asimetria positiva (SK 0.19)
# Platykurtic
#graficamos
#fig = px.box(df, y='Edad', title='Distribucion de edades')
#fig.show()
fig = sp.make_subplots(rows=1, cols=2, shared_yaxes=True, subplot_titles=['Edad', 'EdadAplicacion(calculada)'])
fig.add_trace(px.box(df, y='Edad').data[0], row=1, col=1)
fig.add_trace(px.box(df, y='EdadAplicacion').data[0], row=1, col=2)
fig.update_layout(title='Comparación de Columnas de Edades')
fig.show()
#funcion para encontrar IQR (rango intercuartil)
def find_outlier_IQR(df):
q1=df.quantile(0.25)
q3=df.quantile(0.75)
IQR = q3-q1
print(f'Q1: {q1}, Q3: {q3}, IQR: {IQR}')
outliers = df[ ((df<(q1-1.5*IQR)) | (df>(q3+1.5*IQR))) ]
return outliers
#Revisamos los Rangos intercuartiles de las columnas EDAD y EDAD_APLICACION
outliers = find_outlier_IQR(df['Edad'])
print(f'EDAD\nnúmero de outliers: '+ str(len(outliers)))
print('min valor outlier: ' + str(outliers.min()))
print('max valor outlier: ' + str(outliers.max()))
outliers = find_outlier_IQR(df['EdadAplicacion'])
print(f'EDAD_APLICACION\nnúmero de outliers: '+ str(len(outliers)))
print('min valor outlier: ' + str(outliers.min()))
print('max valor outlier: ' + str(outliers.max()))
Q1: 29.0, Q3: 60.0, IQR: 31.0 EDAD número de outliers: 73 min valor outlier: 107 max valor outlier: 198 Q1: 28.0, Q3: 59.0, IQR: 31.0 EDAD_APLICACION número de outliers: 5 min valor outlier: 106 max valor outlier: 121
Se podria hacer imputacion de outliers, reemplazando con la edad media para valores mayores o menors que los outliers limite definido
VER CODIGO (doble click)
En este caso lo que haremos sera borrar esos outliers <!-- def impute_outliers_IQR(df): q1=df.quantile(0.25) q3=df.quantile(0.75) IQR=q3-q1 upper = df[~(df>(q3+1.5IQR))].max() lower = df[~(df<(q1-1.5IQR))].min()
df = np.where(df > upper, # donde sea mayor df.mean(), # colocamos la media np.where( # en otros caso: df < lower, # Cuando sea menor a lower df.mean(), # colocamos la media df # el resto de las veces dejamos el valor como estaba ) ) return df
df['EdadAplicacion'] = impute_outliers_IQR(df['EdadAplicacion']) df['Edad'] = impute_outliers_IQR(df['Edad']) df.describe()['EdadAplicacion'] -->
def remove_outliers_IQR(df, column): #Procedimiento para limpiar outliers
q1 = df[column].quantile(0.25)
q3 = df[column].quantile(0.75)
IQR = q3 - q1
lower_bound = q1 - 1.5 * IQR
upper_bound = q3 + 1.5 * IQR
print(df[(df[column] < lower_bound) | (df[column] > upper_bound)].index)
df.drop(df[(df[column] < lower_bound) | (df[column] > upper_bound)].index, inplace=True)
remove_outliers_IQR(df, 'Edad')
Int64Index([ 105181, 506228, 506229, 518722, 518723, 518724, 518725,
520918, 524624, 524625, 524626, 534182, 534183, 538607,
538608, 544322, 554794, 554795, 564308, 564309, 578002,
583700, 584612, 593773, 603604, 603605, 611080, 615331,
615332, 616422, 616969, 616970, 617754, 617755, 625248,
631949, 631950, 636193, 636194, 653497, 653498, 679766,
697966, 697967, 697968, 706401, 706402, 727441, 727442,
755423, 755424, 789710, 972153, 978008, 978009, 995359,
1004685, 1012996, 1018821, 1034174, 1071722, 1071723, 1084619,
1084620, 1089720, 1089721, 1106619, 1106620, 1121892, 1121893,
1127742, 1127743, 1133471],
dtype='int64')
fig = sp.make_subplots(rows=1, cols=2, shared_yaxes=True, subplot_titles=['Edad', 'EdadAplicacion(calculada)'])
fig.add_trace(px.box(df, y='Edad').data[0], row=1, col=1)
fig.add_trace(px.box(df, y='EdadAplicacion').data[0], row=1, col=2)
fig.update_layout(title='Comparación de Columnas de Edades')
fig.show()
#graficamos
fig = px.histogram(df,x='Edad',nbins=30)
fig.show()
print(f"Skewness: {df['Edad'].skew()}")
print(f"Kurtosis: {df['Edad'].kurt()}")
Skewness: 0.16871097875694618 Kurtosis: -0.7255114503193738
# Asimetria positiva (SK 0.16)
# platicúrtica
Para el caso de las columnas Edad y EdadAplicacion, se realizo una limpieza de los registros completos que tenian valroes atipicos en las columnas edades, quedado con un total de 113441, es decir, se borraron 73 registros basado en la columna Edad y no la columna EdadCalculda, nota imporante, sería interesante entender la naturaleza de estos outliers.
correlation_mat = df.corr()
mask = np.zeros_like(correlation_mat)
mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
f, ax = plt.subplots(figsize=(13,6))
ax = sns.heatmap(correlation_mat,
mask=mask,annot=True,cmap="YlGnBu")
df_resd = df.copy()
df_resd.plot(kind='box', subplots=True, layout=(4,4), sharex=False, sharey=False, figsize=(15,15), title='Box Plot para las variables de entrada')
#plt.savefig('vino')
plt.show()
#sns.pairplot(df)
df['Sexo'].unique()
array(['MASCULINO', 'FEMENINO', nan, 'NO DEFINIDO', 'INDEFINIDO'],
dtype=object)
valores_nulos_por_columna = df.isna().sum()
columnas_con_valores_no_nulos = valores_nulos_por_columna[valores_nulos_por_columna != 0]
print(columnas_con_valores_no_nulos)
Sexo 488 dtype: int64
Estandarizamos No definido e Indefinido por Indefinido
df["Sexo"] = df["Sexo"].str.replace('NO DEFINIDO', 'INDEFINIDO')
Para un mejor entendimiento se va a observar graficamente:
(df.value_counts('Sexo',normalize=True)*100).round(2).astype(str)+'%'
Sexo FEMENINO 54.51% MASCULINO 45.47% INDEFINIDO 0.03% dtype: object
Puesto que tenemos valores faltantes en las columnas de Sexo, seleccionaremos de manera aleatoria un valor entre los existentes para hacer la asignacion
df['Sexo'].unique()
array(['MASCULINO', 'FEMENINO', nan, 'INDEFINIDO'], dtype=object)
# Calcula la distribución de los valores existentes en la columna 'Sexo'
sexo_distribution = df['Sexo'].value_counts(normalize=True)
# Imputa los valores NaN en función de la distribución
df['Sexo'].fillna(pd.Series(np.random.choice(sexo_distribution.index, p=sexo_distribution.values, size=len(df))), inplace=True)
# Verifica que los valores NaN se hayan reemplazado correctamente
print(df['Sexo'].unique())
['MASCULINO' 'FEMENINO' 'INDEFINIDO']
#Sexo
#FEMENINO 618081
#INDEFINIDO 291
#MASCULINO 515550
#Name: Sexo, dtype: int64
df['Sexo'].groupby(df['Sexo']).count()
Sexo FEMENINO 618351 INDEFINIDO 291 MASCULINO 515768 Name: Sexo, dtype: int64
(df.value_counts('Sexo',normalize=True)*100).round(2).astype(str)+'%'
Sexo FEMENINO 54.51% MASCULINO 45.47% INDEFINIDO 0.03% dtype: object
Existe relación entre la edad de las personas vacunadas y su desenlace (Confirmado, Hospitalizado, Muerto)? Cualitativa y Cuantitativa
# Crear una figura con tres subplots en una fila
fig, axes = plt.subplots(1, 3, figsize=(18, 4))
# Crear el gráfico de cajas y bigotes en el primer subplot
sns.boxplot(x=df["Confirmado"], y=df["EdadAplicacion"], palette='rocket_r', ax=axes[0])
axes[0].set_ylabel('Edades')
axes[0].set_xlabel('')
axes[0].set_title('Casos Confirmados')
# Filtrar datos para cada caja
df_confirmado_si = df[df["Confirmado"] == "Positivo"]
df_confirmado_no = df[df["Confirmado"] == "No"]
# Crear gráfico de barras para Confirmado "Si" en el segundo subplot
sns.histplot(data=df_confirmado_si, x='EdadAplicacion', color='blue', bins=20, ax=axes[1])
axes[1].set_xlabel('Edades')
axes[1].set_ylabel('Frecuencia')
axes[1].set_title('Hist POSITIVO')
# Crear gráfico de barras para Confirmado "No" en el tercer subplot
sns.histplot(data=df_confirmado_no, x='EdadAplicacion', color='red', bins=20, ax=axes[2])
axes[2].set_xlabel('Edades')
axes[2].set_ylabel('Frecuencia')
axes[2].set_title('Hist NO')
# Ajustar espaciado entre subplots
plt.tight_layout()
# Mostrar la figura con los tres gráficos en una fila
plt.show()
Validación de los supuestos (NORMALIDAD)
Verifiquemos primero el supuesto de normalidad. El supuesto establece que las poblaciones deben seguir la distribución normal o tener un tamaño mayor a 30 para que se cumpla el TLC.
Recordemos las hipótesis:
Ho: La muestra proviene de una población que sigue una distribución normal
Ha: La muestra NO proviene de una población que sigue una distribución normal
## QQplots
fig, axs = plt.subplots(ncols=2)
fig.set_size_inches(8, 4)
fig.subplots_adjust(wspace=0.3)
ax1=qqplot(df[df["Confirmado"]=="Positivo"]["EdadAplicacion"], line='s',ax=axs[0])
axs[0].set_title('Positivo')
ax2=qqplot(df[df["Confirmado"]=="No"]["EdadAplicacion"], line='s',ax=axs[1])
axs[1].set_title('No')
plt.show()
# normalidad
print("Población: Confirmado Positivo")
Estad,vp = shapiro(df[df["Confirmado"]=="Positivo"]["Edad"])
print(f'Estadístico SW= {Estad}, Valor-p= {vp}')
Estad,vc,sig=anderson(df[df["Confirmado"]=="Positivo"]["Edad"])
print(f'Estadístico AD= {Estad}, Valor crítico (sign. 5%)= {vc[2]}')
print('')
print("Población: No Confirmado positivo ")
Estad,vp = shapiro(df[df["Confirmado"]=="No"]["Edad"])
print(f'Estadístico SW= {Estad}, Valor-p= {vp}')
Estad,vc,sig=anderson(df[df["Confirmado"]=="No"]["Edad"])
print(f'Estadístico AD= {Estad}, Valor crítico (sign. 5%)= {vc[2]}')
Población: Confirmado Positivo Estadístico SW= 0.9806710481643677, Valor-p= 0.0 Estadístico AD= 1803.0124239556608, Valor crítico (sign. 5%)= 0.787 Población: No Confirmado positivo Estadístico SW= 0.985482931137085, Valor-p= 0.0 Estadístico AD= 4039.2869605114684, Valor crítico (sign. 5%)= 0.787
## distribucion de confirmdos y no confirmados para evaluar la media de ambas
Conclusión: La distribucion o proviene de una dist normal
Validación de los supuestos (HOMOGENEIDAD DE VARIANZAS)
Ahora pasemos al supuesto de homogeneidad de varianzas y recordemos las hipótesis:
Ho: La varianza en todas las poblaciones es igual
Ha: La varianza de al menos 1 es diferente
# Homogeneidad de varianzas
Estad,vp=levene(df_unicos[df_unicos["Confirmado"]=="Positivo"]["EdadAplicacion"],
df_unicos[df_unicos["Confirmado"]=="No"]["EdadAplicacion"],
center='mean')
print(f'Estadístico W= {Estad}, Valor-p= {vp}')
Estadístico W= 7592.293172863293, Valor-p= 0.0
Conclusión: no hay homogenidad de varianzas
Prueba no parametrica
h0 : La distribucion de ambas poblaciones es la misma (misma mediana)
ha : La distribucion de ambas poblaciones es diferente (diferente mediana)
Estad,vp=mannwhitneyu(df_unicos[df_unicos["Confirmado"]=="Positivo"]["EdadAplicacion"],
df_unicos[df_unicos["Confirmado"]=="No"]["EdadAplicacion"])
print(f'Estadístico W= {Estad}, Valor-p= {vp}')
Estadístico W= 22637133770.5, Valor-p= 3.740289196574841e-217
Conclusion : como el valor p es menor que la significancia rechazo Ha
# Crear una figura con tres subplots en una fila
fig, axes = plt.subplots(1, 3, figsize=(18, 4))
# Crear el gráfico de cajas y bigotes en el primer subplot
sns.boxplot(x=df["ServicioMayorComplejidad"], y=df["EdadAplicacion"], palette='rocket_r', ax=axes[0])
axes[0].set_ylabel('Edades')
axes[0].set_xlabel('')
axes[0].set_title('Hospitalizaciones')
# Filtrar datos para cada caja
df_hospitalizado = df[df['ServicioMayorComplejidad'] == 'Hospitalizado']
df_no = df[df['ServicioMayorComplejidad'] == 'No']
# Crear gráfico de barras para Confirmado "Si" en el segundo subplot
sns.histplot(data=df_hospitalizado, x='EdadAplicacion', color='blue', bins=20, ax=axes[1])
axes[1].set_xlabel('Edades')
axes[1].set_ylabel('Frecuencia')
axes[1].set_title('Hist HOSPITALIZADO')
# Crear gráfico de barras para Confirmado "No" en el tercer subplot
sns.histplot(data=df_no, x='EdadAplicacion', color='red', bins=20, ax=axes[2])
axes[2].set_xlabel('Edades')
axes[2].set_ylabel('Frecuencia')
axes[2].set_title('Hist NO')
# Ajustar espaciado entre subplots
plt.tight_layout()
# Mostrar la figura con los tres gráficos en una fila
plt.show()
Validación de los supuestos (NORMALIDAD)
Verifiquemos primero el supuesto de normalidad. El supuesto establece que las poblaciones deben seguir la distribución normal o tener un tamaño mayor a 30 para que se cumpla el TLC.
Recordemos las hipótesis:
Ho: La muestra proviene de una población que sigue una distribución normal
Ha: La muestra NO proviene de una población que sigue una distribución normal
## QQplots
fig, axs = plt.subplots(ncols=2)
fig.set_size_inches(8, 4)
fig.subplots_adjust(wspace=0.3)
ax1=qqplot(df[df["ServicioMayorComplejidad"]=="Hospitalizado"]["EdadAplicacion"], line='s',ax=axs[0])
axs[0].set_title('Hospitalizado')
ax2=qqplot(df[df["ServicioMayorComplejidad"]=="No"]["EdadAplicacion"], line='s',ax=axs[1])
axs[1].set_title('No')
plt.show()
# normalidad
print("Población: Hospitalizado")
Estad,vp = shapiro(df[df["ServicioMayorComplejidad"]=="Hospitalizado"]["EdadAplicacion"])
print(f'Estadístico SW= {Estad}, Valor-p= {vp}')
Estad,vc,sig=anderson(df[df["ServicioMayorComplejidad"]=="Hospitalizado"]["EdadAplicacion"])
print(f'Estadístico AD= {Estad}, Valor crítico (sign. 5%)= {vc[2]}')
print('')
print("Población: No Hospitalizado")
Estad,vp = shapiro(df[df["ServicioMayorComplejidad"]=="No"]["EdadAplicacion"])
print(f'Estadístico SW= {Estad}, Valor-p= {vp}')
Estad,vc,sig=anderson(df[df["ServicioMayorComplejidad"]=="No"]["EdadAplicacion"])
print(f'Estadístico AD= {Estad}, Valor crítico (sign. 5%)= {vc[2]}')
Población: Hospitalizado Estadístico SW= 0.9816091060638428, Valor-p= 0.0 Estadístico AD= 837.409109200089, Valor crítico (sign. 5%)= 0.787 Población: No Hospitalizado Estadístico SW= 0.9861602783203125, Valor-p= 0.0 Estadístico AD= 4321.834139822866, Valor crítico (sign. 5%)= 0.787
Conclusión: no hay normalidad
Validación de los supuestos (HOMOGENEIDAD DE VARIANZAS)
Ahora pasemos al supuesto de homogeneidad de varianzas y recordemos las hipótesis:
Ho: La varianza en todas las poblaciones es igual
Ha: La varianza de al menos 1 es diferente
# Homogeneidad de varianzas
Estad,vp=levene(df[df["ServicioMayorComplejidad"]=="Hospitalizado"]["EdadAplicacion"],
df[df["ServicioMayorComplejidad"]=="No"]["EdadAplicacion"],
center='mean')
print(f'Estadístico W= {Estad}, Valor-p= {vp}')
Estadístico W= 6599.015596648111, Valor-p= 0.0
Estad,vp=ttest_ind(df[df["ServicioMayorComplejidad"]=="Hospitalizado"]["EdadAplicacion"],
df[df["ServicioMayorComplejidad"]=="No"]["EdadAplicacion"],
equal_var=False)
print(f'Media grupo 1:{round(df[df["ServicioMayorComplejidad"]=="Hospitalizado"]["EdadAplicacion"].mean(),2)}, Media grupo 2:{round(df[df["ServicioMayorComplejidad"]=="No"]["EdadAplicacion"].mean(),2)}, Estadístico t= {Estad}, Valor-p= {vp}')
Media grupo 1:43.67, Media grupo 2:43.46, Estadístico t= 3.925659380989586, Valor-p= 8.65238266075685e-05
Conclusion : rechazo la h0, por tanto la varianza de al menos 1 es diferente, teniendo en cuenta que valor p es 0.0007 hay diferencia significativa entre las poblaciones
# Mann-Whitney U
H0: No hay diferencia significativa entre los dos grupos poblacionales
Ha: Hay una diferencia significativa entre los dos grupos poblacionales (las distribuciones de ambos grupos son diferentes).
Estad,vp=mannwhitneyu(df_unicos[df_unicos["ServicioMayorComplejidad"]=="Hospitalizado"]["EdadAplicacion"],
df_unicos[df_unicos["ServicioMayorComplejidad"]=="No"]["EdadAplicacion"])
print(f'Estadístico W= {Estad}, Valor-p= {vp}')
Estadístico W= 13029501818.0, Valor-p= 2.85443841538703e-110
Conclusion : rechazamos la hipotesis nula, por tanto hay diferencia significativa entre los hospitalziados y no hospitizalidso
# Crear una figura con tres subplots en una fila
fig, axes = plt.subplots(1, 3, figsize=(18, 4))
# Crear el gráfico de cajas y bigotes en el primer subplot
sns.boxplot(x=df["NDEstadoVital"], y=df["EdadAplicacion"], palette='rocket_r', ax=axes[0])
axes[0].set_ylabel('Edades')
axes[0].set_xlabel('')
axes[0].set_title('ESTADO')
# Filtrar datos para cada caja
df_si = df[df['NDEstadoVital'] == 'VIVO']
df_no = df[df['NDEstadoVital'] == 'MUERTO']
# Crear gráfico de barras para Confirmado "Si" en el segundo subplot
sns.histplot(data=df_si, x='EdadAplicacion', color='blue', bins=20, ax=axes[1])
axes[1].set_xlabel('Edades')
axes[1].set_ylabel('Frecuencia')
axes[1].set_title('Hist VIVOS')
# Crear gráfico de barras para Confirmado "No" en el tercer subplot
sns.histplot(data=df_no, x='EdadAplicacion', color='red', bins=20, ax=axes[2])
axes[2].set_xlabel('Edades')
axes[2].set_ylabel('Frecuencia')
axes[2].set_title('Hist MUERTO')
# Ajustar espaciado entre subplots
plt.tight_layout()
# Mostrar la figura con los tres gráficos en una fila
plt.show()
Validación de los supuestos (NORMALIDAD)
Verifiquemos primero el supuesto de normalidad. El supuesto establece que las poblaciones deben seguir la distribución normal o tener un tamaño mayor a 30 para que se cumpla el TLC.
Recordemos las hipótesis:
Ho: La muestra proviene de una población que sigue una distribución normal
Ha: La muestra NO proviene de una población que sigue una distribución normal
## QQplots
fig, axs = plt.subplots(ncols=2)
fig.set_size_inches(8, 4)
fig.subplots_adjust(wspace=0.3)
ax1=qqplot(df[df["NDEstadoVital"]=="MUERTO"]["EdadAplicacion"], line='s',ax=axs[0])
axs[0].set_title('Fallecido')
ax2=qqplot(df[df["NDEstadoVital"]=="VIVO"]["EdadAplicacion"], line='s',ax=axs[1])
axs[1].set_title('Vivo')
plt.show()
# normalidad
print("Población: Fallecido")
Estad,vp = shapiro(df[df["NDEstadoVital"]=="MUERTO"]["EdadAplicacion"])
print(f'Estadístico SW= {Estad}, Valor-p= {vp}')
Estad,vc,sig=anderson(df[df["NDEstadoVital"]=="MUERTO"]["EdadAplicacion"])
print(f'Estadístico AD= {Estad}, Valor crítico (sign. 5%)= {vc[2]}')
print('')
print("Población: Vivo")
Estad,vp = shapiro(df[df["NDEstadoVital"]=="VIVO"]["EdadAplicacion"])
print(f'Estadístico SW= {Estad}, Valor-p= {vp}')
Estad,vc,sig=anderson(df[df["NDEstadoVital"]=="VIVO"]["EdadAplicacion"])
print(f'Estadístico AD= {Estad}, Valor crítico (sign. 5%)= {vc[2]}')
Población: Fallecido Estadístico SW= 0.9159862995147705, Valor-p= 0.0 Estadístico AD= 222.8300510199515, Valor crítico (sign. 5%)= 0.787 Población: Vivo Estadístico SW= 0.9839651584625244, Valor-p= 0.0 Estadístico AD= 4658.138550783973, Valor crítico (sign. 5%)= 0.787
Conclusion : las distribuciones no son normales, dado que el valor p es menos a la significancia
Validación de los supuestos (HOMOGENEIDAD DE VARIANZAS)
Ahora pasemos al supuesto de homogeneidad de varianzas y recordemos las hipótesis:
Ho: La varianza en todas las poblaciones es igual
Ha: La varianza de al menos 1 es diferente
# Homogeneidad de varianzas
Estad,vp=levene(df[df["NDEstadoVital"]=="MUERTO"]["EdadAplicacion"],
df[df["NDEstadoVital"]=="VIVO"]["EdadAplicacion"],
center='mean')
print(f'Estadístico W= {Estad}, Valor-p= {vp}')
Estadístico W= 2044.7263386218026, Valor-p= 0.0
Conclusion : Rechazo la h0, por tanto, la varianza de almenos 1 es diferente
Estad,vp=ttest_ind(df[df["NDEstadoVital"]=="MUERTO"]["EdadAplicacion"],
df[df["NDEstadoVital"]=="VIVO"]["EdadAplicacion"],
equal_var=False)
print(f'Media grupo 1:{round(df[df["NDEstadoVital"]=="MUERTO"]["EdadAplicacion"].mean(),2)}, Media grupo 2:{round(df[df["NDEstadoVital"]=="VIVO"]["EdadAplicacion"].mean(),2)}, Estadístico t= {Estad}, Valor-p= {vp}')
Media grupo 1:75.83, Media grupo 2:43.18, Estadístico t= 215.46345152659362, Valor-p= 0.0
#Dado que no hay normalidad, realizamos prueba no paramétrica de Mann-Whitney-Wilcoxon:
Estad,vp=mannwhitneyu(df[df["NDEstadoVital"]=="MUERTO"]["EdadAplicacion"],
df[df["NDEstadoVital"]=="VIVO"]["EdadAplicacion"])
print(f'Estadístico W= {Estad}, Valor-p= {vp}')
Estadístico W= 10688816539.0, Valor-p= 0.0
Conclusion : Rechazo la h0
Existe relacion entre el desenlace (Confirmado, Hospitalizado, Fallecido) y el sexo de la persona vacunada?
df['Sexo'].unique()
array(['MASCULINO', 'FEMENINO', 'INDEFINIDO'], dtype=object)
df['Confirmado'].unique()
array(['Positivo', 'No'], dtype=object)
df['ServicioMayorComplejidad'].unique()
array(['No', 'Hospitalizado'], dtype=object)
df['NDEstadoVital'].unique()
array(['VIVO', 'MUERTO'], dtype=object)
df_selec = df[(df['Sexo']=='FEMENINO') | (df['Sexo']=='MASCULINO')]
tabla_frec=pd.crosstab(index=df_selec["Sexo"],columns=df_selec["Confirmado"],normalize="index").reset_index()
tabla_frec
| Confirmado | Sexo | No | Positivo |
|---|---|---|---|
| 0 | FEMENINO | 0.76 | 0.24 |
| 1 | MASCULINO | 0.77 | 0.23 |
fig_sns = plt.figure()
ax = fig_sns.add_axes([0,0,1,1])
ax=sns.barplot(x=tabla_frec["Sexo"],y=tabla_frec["Positivo"]*100,order=tabla_frec.sort_values(by='No')["Sexo"])
ax.bar_label(ax.containers[0], label_type='edge',fmt='%.2f%%', padding=5)
ax.set(ylim=(0, 25))
plt.ylabel('Frec.Rel.')
plt.xlabel('')
plt.show()
Prueba de Chi Cuadrado de independencia
Recordemos las hipótesis:
tabla_frec_abs=pd.crosstab(index=df_selec["Sexo"],columns=df_selec["Confirmado"])
Estad,vp,gl,frec_esp=chi2_contingency(tabla_frec_abs)
print(f'Estadístico X^2= {Estad}, Valor-p= {vp}')
Estadístico X^2= 391.26633943171714, Valor-p= 4.38727407560172e-87
Conclusion: Tenemos evidencia suficiente para determinar que existe relacion entre confirmados y el sexo (se rechaza h0)
tabla_frec=pd.crosstab(index=df_selec["Sexo"],columns=df_selec["ServicioMayorComplejidad"],normalize="index").reset_index()
tabla_frec
| ServicioMayorComplejidad | Sexo | Hospitalizado | No |
|---|---|---|---|
| 0 | FEMENINO | 0.12 | 0.88 |
| 1 | MASCULINO | 0.11 | 0.89 |
fig_sns = plt.figure()
ax = fig_sns.add_axes([0,0,1,1])
ax=sns.barplot(x=tabla_frec["Sexo"],y=tabla_frec["Hospitalizado"]*100,order=tabla_frec.sort_values(by='No')["Sexo"])
ax.bar_label(ax.containers[0], label_type='edge',fmt='%.2f%%', padding=5)
ax.set(ylim=(0, 13))
plt.ylabel('Frec.Rel.')
plt.xlabel('')
plt.show()
Prueba de Chi Cuadrado de independencia
Recordemos las hipótesis:
tabla_frec_abs=pd.crosstab(index=df_selec["Sexo"],columns=df_selec["ServicioMayorComplejidad"])
Estad,vp,gl,frec_esp=chi2_contingency(tabla_frec_abs)
print(f'Estadístico X^2= {Estad}, Valor-p= {vp}')
Estadístico X^2= 185.24062757437062, Valor-p= 3.4774996176279596e-42
Conclusion: Tenemos evidencia suficiente para determinar que existe relacion entre confirmados y el sexo (se rechaza h0)
tabla_frec=pd.crosstab(index=df_selec["Sexo"],columns=df_selec["NDEstadoVital"],normalize="index").reset_index()
tabla_frec
| NDEstadoVital | Sexo | MUERTO | VIVO |
|---|---|---|---|
| 0 | FEMENINO | 0.01 | 0.99 |
| 1 | MASCULINO | 0.01 | 0.99 |
fig_sns = plt.figure()
ax = fig_sns.add_axes([0,0,1,1])
ax=sns.barplot(x=tabla_frec["Sexo"],y=tabla_frec["MUERTO"]*100,order=tabla_frec.sort_values(by='VIVO')["Sexo"])
ax.bar_label(ax.containers[0], label_type='edge',fmt='%.2f%%', padding=5)
ax.set(ylim=(0, 1.25))
plt.ylabel('Frec.Rel.')
plt.xlabel('')
plt.show()
Prueba de Chi Cuadrado de independencia
Recordemos las hipótesis:
tabla_frec_abs=pd.crosstab(index=df_selec["Sexo"],columns=df_selec["NDEstadoVital"])
Estad,vp,gl,frec_esp=chi2_contingency(tabla_frec_abs)
print(f'Estadístico X^2= {Estad}, Valor-p= {vp}')
Estadístico X^2= 110.85573988448668, Valor-p= 6.363703081076376e-26
Conclusion: Tenemos evidencia suficiente para determinar que existe relacion entre estado y el sexo (se rechaza h0)